# ============================================================================
# 
#  Portlet.py
#  KnowledgeEngine
#  
#  Created by John Meredith on 2007-02-20.
#  Copyright 2007 Faulkner Technologies. All rights reserved.
# 
# ============================================================================


# ============================================================================
# Zope Library Imports
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager
from App.ImageFile import ImageFile

# ============================================================================
# Python Library Imports

# ============================================================================
# CMF Library Imports
from Products.CMFCore import permissions
from Products.CMFCore.utils import getToolByName

# ============================================================================
# Knowledge Engine Library Imports
from GenericObject import GenericObject, GenericObjectInfo
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *

# ============================================================================
# Portlet Constants
# ============================================================================
PORTLET_SLOTS = [ ('Left', 'left_slots'), ('Right', 'right_slots') ]


# ============================================================================
# Add portlets to the current context location. This was initially in the KE
# install process
# ============================================================================
def addPortletSlots(context):
    slot = "here/KnowledgeEnginePortlets/macros/"

    portal_url = getToolByName(context, "portal_url")
    portal = portal_url.getPortalObject()

    left_slots = getattr(portal, 'left_slots', None)
    if left_slots != None:
        leftSlot = slot + 'left'
        if leftSlot not in left_slots:
            left_slots = list(left_slots) + [leftSlot]
            portal.left_slots = left_slots

    right_slots = getattr(portal, 'right_slots', None)
    if right_slots != None:
        rightSlot = slot + 'right'
        if rightSlot not in right_slots:
            right_slots = list(right_slots) + [rightSlot]
            portal.right_slots = right_slots


# ============================================================================
# PortletInfo initilaization
# ============================================================================
class PortletInfo(GenericObjectInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """

    # ------------------------------------------------------------------------
    def getClassName(self):
        """
            Returns the name of the class for which
            we are providing information.
        """
        return 'Portlet'


    # ------------------------------------------------------------------------
    def getClassFamilyName(self):
        """
            Return the name of the family to which the the
            class (and subclasses) and it's subclasses belong/
            
            This is used to enable searching for families of 
            generic objects, eg: 'EntitySource' instances.
        """

        # All entity sources belong to the 'Portlet' family
        return 'Portlet'


    # ------------------------------------------------------------------------
    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """

        return "Portlet"


    # ------------------------------------------------------------------------
    def getDescription(self):
        """
            ABSTRACT: Return a friendly description on the purpose
            of this class.  This description is often used in the
            Property Editor for instances of this class
        """

        return "Defines a Plone portlet specific to KnowledgeEngine"


    # ------------------------------------------------------------------------
    def getIdentityType(self):
        """
            Return the identity type.
        """

        return "Portlet"


    # ------------------------------------------------------------------------
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """

        return ImageFile('skins/Portlet_icon.png', globals())


    # ------------------------------------------------------------------------
    def isContainer(self):
        """
            Returns if the instance is providing folderish/container
            services.

            Override an return true if the instance is to be
            a container for other instances.

            NOTE: all instances support "events" so there is no
            need to return true if you require "event" support.
        """

        return false


    # ------------------------------------------------------------------------
    def isPermittedInContainer(self, containerClassInfo, containerInstance = None, instance=None):
        """
            ABSTRACT: Returns if class instances (instance) is permitted in
            the specified container (containerInstance)

            NOTE: This is from this class' instance perspective.
        """

        #TODO: EntitySources are only permitted in CMF Folders!!!
        return false


    # ------------------------------------------------------------------------
    def isChildPermitted(self, childClassInfo, childInstance = None):
        """
            Returns if the specified child is permitted as a
            child of the class' instances.

            NOTE: This is from this class' instance perspective.

            For component containers, this always overrides what the
            return value from childClassInfo.isPermittedInContainer(...)
        """

        #EntitySources don't contain anything
        return false


    # ------------------------------------------------------------------------
    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific properties using the PropertyReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class properties for reflection
        GenericObjectInfo.onConfigureProperties(self)

        self.addPropertyDefinition(
            PropertyDefinition(
                identity="displayPosition",
                displayName="Display Position",
                description="Position this portlet is to be displayed",
                defaultValue=[],
                propertyType=pt_NATIVE,
                propertyStructure=ps_SET,
                propertyFormat=pf_NATIVE,
                hasEnumeratedValues=true,
                enumeratedValuesMapping=PORTLET_SLOTS,
                constructorEVAP=evap_READWRITE,
                propertyEditorEVAP=evap_READWRITE
            )
        )


    # ------------------------------------------------------------------------
    def onConfigureEvents(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific events using the EventReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class events for reflection
        GenericObjectInfo.onConfigureEvents(self)


    # ------------------------------------------------------------------------
    def onConfigurePermissions(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific permission definitions.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class permissions for reflection
        GenericObjectInfo.onConfigurePermissions(self)


# ============================================================================
# Portlet initilaization
# ============================================================================
class Portlet(GenericObject):
    """
        Base container for all Faulkner KnowledgeEngine Portlets
    """

    security = ClassSecurityInfo()

    # ------------------------------------------------------------------------
    # ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info
    # about this class
    _CLASSINFO = PortletInfo()

    # ------------------------------------------------------------------------
    # Default Attribute Values
    meta_type = 'Portlet'


    # ------------------------------------------------------------------------
    def __init__(self, id, identity):
        """
            Constructor for the object
        """

        GenericObject.__init__(self, id, identity)

        #perform onAfterInstanciation
        self.onAfterInstanciation()


    # ------------------------------------------------------------------------
    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object
            has been loaded into memory.

            This is useful for performing backwards compatibility
        """

        #perform default onAfterInstanciation for the container
        GenericObject.onAfterInstanciation(self)


    # ------------------------------------------------------------------------
    def getPortalObject(self):
        """
            Return the portal level object (like a Model or EntitySource)
            in which this object is located
        """

        return self


    # ------------------------------------------------------------------------
    def hasDefaultView(self):
        """
            PLONE Support:

            Returns if the object has a default view.  By setting this
            to true, a view tab will be added to the object.

            If this is false, attempting to visit the / (default view)
            for a component will redirect the client to the
            next available parent that has a default view.
        """

        return false


    # ------------------------------------------------------------------------
    def getDisplayPosition(self):
        """
            Returns the position (left or right) where this portlet is to be
            displayed
        """

        return getattr(self, 'displayPosition', [])


    # ------------------------------------------------------------------------
    def setDisplayPosition(self, newValue):
        """
            Sets the position (left or right) where this portlet is to be
            displayed.
        """

        # Do nothing if the newValue is not in the PORTLET_SLOTS
        if newValue not in [slot[1] for slot in PORTLET_SLOTS]:
            return
        
        # Do nothing if the old and new values are identical
        if newValue == self.getDisplayPosition():
            return

        self.displayPosition = newValue


    # ------------------------------------------------------------------------
    def getContents(self, REQUEST, **args):
        """
            Returns the content of the specific portlet. This should be
            overridden in subclasses i.e. MyKnowledgePortlet
        """

        raise NotImplementedError, "This should be implemented in a subclass."
